/*
 * Copyright 2011 Katsunori Koyanagi
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package sweezy.swf.tag
{
	import sweezy.swf.IElement;
	import sweezy.swf.ITag;
	import sweezy.swf.constant.ElementType;
	import sweezy.swf.utils.CloneUtils;
	import sweezy.swf.visitor.ISWFElementVisitor;

	public class Tag implements ITag
	{

		public static const CSM_TEXT_SETTINGS:uint = 74;

		public static const DEBUG_ID:uint = 63;

		public static const DEFINE_BINARY_DATA:uint = 87;

		public static const DEFINE_BITS:uint = 6;

		public static const DEFINE_BITS_JPEG_2:uint = 21;

		public static const DEFINE_BITS_JPEG_3:uint = 35;

		public static const DEFINE_BITS_JPEG_4:uint = 90;

		public static const DEFINE_BITS_LOSSLESS:uint = 20;

		public static const DEFINE_BITS_LOSSLESS_2:uint = 36;

		public static const DEFINE_BUTTON:uint = 7;

		public static const DEFINE_BUTTON_2:uint = 34;

		public static const DEFINE_BUTTON_CXFORM:uint = 23;

		public static const DEFINE_BUTTON_SOUND:uint = 17;

		public static const DEFINE_EDIT_TEXT:uint = 37;

		public static const DEFINE_FONT:uint = 10;

		public static const DEFINE_FONT_2:uint = 48;

		public static const DEFINE_FONT_3:uint = 75;

		public static const DEFINE_FONT_4:uint = 91;

		public static const DEFINE_FONT_ALIGN_ZONES:uint = 73;

		public static const DEFINE_FONT_INFO:uint = 13;

		public static const DEFINE_FONT_INFO_2:uint = 62;

		public static const DEFINE_FONT_NAME:uint = 88;

		public static const DEFINE_MORPH_SHAPE:uint = 46;

		public static const DEFINE_MORPH_SHAPE_2:uint = 84;

		public static const DEFINE_SCALING_GRID:uint = 78;

		public static const DEFINE_SCENE_AND_FRAME_LABEL_DATA:uint = 86;

		public static const DEFINE_SHAPE:uint = 2;

		public static const DEFINE_SHAPE_2:uint = 22;

		public static const DEFINE_SHAPE_3:uint = 32;

		public static const DEFINE_SHAPE_4:uint = 83;

		public static const DEFINE_SOUND:uint = 14;

		public static const DEFINE_SPRITE:uint = 39;

		public static const DEFINE_TEXT:uint = 11;

		public static const DEFINE_TEXT_2:uint = 33;

		public static const DEFINE_VIDEO_STREAM:uint = 60;

		public static const DO_ABC:uint = 72;

		public static const DO_ABC2:uint = 82;

		public static const DO_ACTION:uint = 12;

		public static const DO_INIT_ACTION:uint = 59;

		public static const ENABLE_DEBUGGER:uint = 58;

		public static const ENABLE_DEBUGGER_2:uint = 64;

		public static const END:uint = 0;

		public static const EXPORT_ASSETS:uint = 56;

		public static const FILE_ATTRIBUTES:uint = 69;

		public static const FRAME_LABEL:uint = 43;

		public static const IMPORT_ASSETS:uint = 57;

		public static const IMPORT_ASSETS_2:uint = 71;

		public static const JPEG_TABLES:uint = 8;

		public static const METADATA:uint = 77;

		public static const PLACE_OBJECT:uint = 4;

		public static const PLACE_OBJECT_2:uint = 26;

		public static const PLACE_OBJECT_3:uint = 70;

		public static const PRODUCT_INFO:uint = 41;

		public static const PROTECT:uint = 24;

		public static const REMOVE_OBJECT:uint = 5;

		public static const REMOVE_OBJECT_2:uint = 28;

		public static const SCRIPT_LIMITS:uint = 65;

		public static const SET_BACKGROUND_COLOR:uint = 9;

		public static const SET_TAB_INDEX:uint = 66;

		public static const SHOW_FRAME:uint = 1;

		public static const SOUND_STREAM_BLOCK:uint = 19;

		public static const SOUND_STREAM_HEAD:uint = 18;

		public static const SOUND_STREAM_HEAD_2:uint = 45;

		public static const START_SOUND:uint = 15;

		public static const START_SOUND_2:uint = 89;

		public static const SYMBOL_CLASS:uint = 76;

		public static const VIDEO_FRAME:uint = 61;

		private static var _tagNames:Object = (function():Object
		{
			var obj:Object = {};

			obj[0] = "End";
			obj[1] = "ShowFrame";
			obj[2] = "DefineShape";
			obj[4] = "PlaceObject";
			obj[5] = "RemoveObject";
			obj[6] = "DefineBits";
			obj[7] = "DefineButton";
			obj[8] = "JPEGTables";
			obj[9] = "SetBackgroundColor";
			obj[10] = "DefineFont";
			obj[11] = "DefineText";
			obj[12] = "DoAction";
			obj[13] = "DefineFontInfo";
			obj[14] = "DefineSound";
			obj[15] = "StartSound";
			obj[17] = "DefineButtonSound";
			obj[18] = "SoundStreamHead";
			obj[19] = "SoundStreamBlock";
			obj[20] = "DefineBitsLossless";
			obj[21] = "DefineBitsJPEG2";
			obj[22] = "DefineShape2";
			obj[23] = "DefineButtonCxform";
			obj[24] = "Protect";
			obj[26] = "PlaceObject2";
			obj[28] = "RemoveObject2";
			obj[32] = "DefineShape3";
			obj[33] = "DefineText2";
			obj[34] = "DefineButton2";
			obj[35] = "DefineBitsJPEG3";
			obj[36] = "DefineBitsLossless2";
			obj[37] = "DefineEditText";
			obj[39] = "DefineSprite";
			obj[41] = "ProductInfo";
			obj[43] = "FrameLabel";
			obj[45] = "SoundStreamHead2";
			obj[46] = "DefineMorphShape";
			obj[48] = "DefineFont2";
			obj[56] = "ExportAssets";
			obj[57] = "ImportAssets";
			obj[58] = "EnableDebugger";
			obj[59] = "DoInitAction";
			obj[60] = "DefineVideoStream";
			obj[61] = "VideoFrame";
			obj[62] = "DefineFontInfo2";
			obj[63] = "DebugID";
			obj[64] = "EnableDebugger2";
			obj[65] = "ScriptLimits";
			obj[66] = "SetTabIndex";
			obj[69] = "FileAttributes";
			obj[70] = "PlaceObject3";
			obj[71] = "ImportAssets2";
			obj[72] = "DoABC";
			obj[73] = "DefineFontAlignZones";
			obj[74] = "CSMTextSettings";
			obj[75] = "DefineFont3";
			obj[76] = "SymbolClass";
			obj[77] = "Metadata";
			obj[78] = "DefineScalingGrid";
			obj[82] = "DoABC2";
			obj[83] = "DefineShape4";
			obj[84] = "DefineMorphShape2";
			obj[86] = "DefineSceneAndFrameLabelData";
			obj[87] = "DefineBinaryData";
			obj[88] = "DefineFontName";
			obj[89] = "StartSound2";
			obj[90] = "DefineBitsJPEG4";
			obj[91] = "DefineFont4";

			//unknown tags
			obj[3] = "FreeCharacter";
			obj[16] = "StopSound";
			obj[25] = "PathsArePostScript";
			obj[29] = "SyncFrame";
			obj[31] = "FreeAll";
			obj[38] = "DefineVideo";
			obj[40] = "NameCharacter";
			obj[42] = "DefineTextFormat";
			obj[44] = "DefineBehavior";
			obj[47] = "FrameTag";
			obj[49] = "GenCommand";
			obj[50] = "DefineCommandObj";
			obj[51] = "CharacterSet";
			obj[52] = "FontRef";
			obj[53] = "DefineFunction";
			obj[54] = "PlaceFunction";
			obj[55] = "GenTagObject";
			obj[67] = "DefineShape4";
			obj[68] = "DefineMorphShape2";

			return obj;
		})();

		private static var _tagVersions:Object = (function():Object
		{
			var obj:Object = {};

			obj[0] = 1;
			obj[1] = 1;
			obj[2] = 1;
			obj[4] = 1;
			obj[5] = 1;
			obj[6] = 1;
			obj[7] = 1;
			obj[8] = 1;
			obj[9] = 1;
			obj[10] = 1;
			obj[11] = 1;
			obj[12] = 1;
			obj[13] = 1;
			obj[14] = 1;
			obj[15] = 1;
			obj[17] = 2;
			obj[18] = 1;
			obj[19] = 1;
			obj[20] = 2;
			obj[21] = 2;
			obj[22] = 2;
			obj[23] = 2;
			obj[24] = 2;
			obj[26] = 3;
			obj[28] = 3;
			obj[32] = 3;
			obj[33] = 3;
			obj[34] = 3;
			obj[35] = 3;
			obj[36] = 3;
			obj[37] = 4;
			obj[39] = 3;
			obj[41] = 3;
			obj[43] = 3;
			obj[45] = 3;
			obj[46] = 3;
			obj[48] = 3;
			obj[56] = 5;
			obj[57] = 5;
			obj[58] = 5;
			obj[59] = 6;
			obj[60] = 6;
			obj[61] = 6;
			obj[62] = 6;
			obj[63] = 6;
			obj[64] = 6;
			obj[65] = 7;
			obj[66] = 7;
			obj[69] = 8;
			obj[70] = 8;
			obj[71] = 8;
			obj[72] = 9;
			obj[73] = 8;
			obj[74] = 8;
			obj[75] = 8;
			obj[76] = 9;
			obj[77] = 1;
			obj[78] = 8;
			obj[82] = 9;
			obj[83] = 8;
			obj[84] = 8;
			obj[86] = 9;
			obj[87] = 9;
			obj[88] = 9;
			obj[89] = 9;
			obj[90] = 10;
			obj[91] = 10;

			//unknown tags
			obj[3] = 1;
			obj[16] = 1;
			obj[25] = 2;
			obj[29] = 3;
			obj[31] = 3;
			obj[38] = 4;
			obj[40] = 3;
			obj[42] = 3;
			obj[44] = 5;
			obj[47] = 3;
			obj[49] = 3;
			obj[50] = 3;
			obj[51] = 3;
			obj[52] = 3;
			obj[53] = 5;
			obj[54] = 5;
			obj[55] = 5;
			obj[67] = 8;
			obj[68] = 8;

			return obj;
		})();

		private static var _unknownTags:Object = (function():Object
		{
			var obj:Object = {};

			obj[3] = null;
			obj[16] = null;
			obj[25] = null;
			obj[29] = null;
			obj[31] = null;
			obj[38] = null;
			obj[40] = null;
			obj[42] = null;
			obj[44] = null;
			obj[47] = null;
			obj[49] = null;
			obj[50] = null;
			obj[51] = null;
			obj[52] = null;
			obj[53] = null;
			obj[54] = null;
			obj[55] = null;
			obj[67] = null;
			obj[68] = null;

			return obj;
		})();

		public static function getKnownTagCodes():Array
		{
			var tagCodes:Array = [];
			for (var key:String in _tagNames)
			{
				if (key in _unknownTags)
				{
					continue;
				}

				tagCodes[tagCodes.length] = uint(key);
			}

			return tagCodes;
		}

		public static function getKnownTagNames():Array
		{
			var tagNames:Array = [];
			for (var key:String in _tagNames)
			{
				if (key in _unknownTags)
				{
					continue;
				}

				tagNames[tagNames.length] = _tagNames[key];
			}

			return tagNames;
		}

		public static function getTagNameByCode(code:uint):String
		{
			if (code in _tagNames)
			{
				return _tagNames[code];
			}

			return code + " (invalid)";
		}

		public static function getTagNames():Array
		{
			var tagNames:Array = [];
			for each (var tagName:String in _tagNames)
			{
				tagNames[tagNames.length] = tagName;
			}

			return tagNames;
		}

		public static function getTagVersionByCode(code:uint):uint
		{
			if (code in _tagVersions)
			{
				return _tagVersions[code];
			}

			return 0;
		}

		private var _code:uint;

		private var _length:uint;

		private var _longHeader:Boolean;

		private var _name:String;

		private var _position:uint;

		private var _tagVersion:uint;

		public function Tag(code:uint)
		{
			_code = code;
			_name = getTagNameByCode(code);
			_tagVersion = getTagVersionByCode(code);
		}

		public function accept(visitor:ISWFElementVisitor):void
		{
			visitor["visit" + elementName](this);
		}

		public function clone():IElement
		{
			var result:Tag;

			if (this is UnknownTag)
			{
				var src:UnknownTag = this as UnknownTag;
				var dst:UnknownTag = new UnknownTag(_code);
				dst.bytes = CloneUtils.clone(src.bytes);
			}
			else
			{
				result = CloneUtils.createClone(this) as Tag;
			}

			result._longHeader = _longHeader;
			result._length = _length;
			result._position = _position;

			return result;
		}

		public function get code():uint
		{
			return _code;
		}

		public function get elementName():String
		{
			return _name;
		}

		public function get elementType():uint
		{
			return ElementType.TAG;
		}

		public function get length():uint
		{
			return _length;
		}

		public function set length(value:uint):void
		{
			_length = value;
		}

		public function get longHeader():Boolean
		{
			return _longHeader;
		}

		public function set longHeader(value:Boolean):void
		{
			_longHeader = value;
		}

		public function get position():uint
		{
			return _position;
		}

		public function set position(value:uint):void
		{
			_position = value;
		}

		public function get tagVersion():uint
		{
			return _tagVersion;
		}

		public function get unknown():Boolean
		{
			return false;
		}
	}
}
